#!/usr/bin/env python

# Some classes for parsing and analyzing URL from Gmail (and possibly other Google
# Applications) from Proxy Logs such as created by grinder

import sys,cgi,pprint,types

def double_quote(s):
    return  '\"' + s + '\"'

class DotGraph:
    def __init__(self,name="unnamed"):
        self.text = ""
        
    def add_branch(self,h,r):
        branch_txt = ""
        for n in h.keys():
            branch_txt += double_quote(r) + '->'  + double_quote(n) + ';\n'
            for o in h[n]:
                print double_quote(n) + ' -> ' + double_quote(o) + ';\n'
        self.text += branch_txt
        


class Request:
    def __init__(self,line):
        self.line = line
        self.method = line.split()[0].rstrip()
        chunked = line.split()[1].split('?')
        request = [ chunked[0], cgi.parse_qsl(chunked[1]) ] 

        self.path = request[0].rstrip()
        self.params = {}
        self.paramlist = []
        self.debug = True 

        for p in request[1]:
            if not self.params.has_key(p[0]):
                self.params[p[0]] = []

            self.params[p[0]].append(p[1])
            self.paramlist.append(p[0])

    # What is the point? I miss Ruby
    def param_keys(self):
        return self.params.keys()

    def param_value(self,key):
        if self.params.has_key(key):
            if len(self.params[key]) == 1:
                return self.params[key][0]
            else:
                return self.params[key]

    def param_count(self):
        return len(self.params.keys())

    def __repr__(self):
        return self.line 

class ParameterMap:
    def __init__(self):
        self.requests = []
        self.paths = []
        self.params = []
        self.histogram = {}

    def request_count(self):
        return len(self.requests)

    def add_request(self,r,preprocess=True):
        self.requests.append(r)
        if r.path not in self.paths:
            self.paths.append(r.path)

            # Get some stats
            if preprocess:
                if self.histogram.has_key(r.param_count()):
                    self.histogram[r.param_count()] =+ 1
                else:
                    self.histogram[r.param_count()] = 1

    def param_map(self,path=None):
        """Create a parameter map for all known requests"""

        param_table = {}

        for r in self.requests:
            # Horribly inefficent, skip if we are doing filtering
            if path:
                if r.path != path:
                    continue 
            for p in r.param_keys():
                if not param_table.has_key(p):
                    param_table[p] = []
                if r.param_value(p) not in param_table[p]:
                    peepee = r.param_value(p)
                    if type(peepee) is types.ListType:
                        for q in peepee:
                            param_table[p].append(q)
                    else:
                        param_table[p].append(peepee)

        return param_table

    def to_dot(self,sanitized=False):
        dg = DotGraph()
        for p in self.paths:
            dg.add_branch(self.param_map(p),p)
        print dg.text

###########################################################################

verbose = False

if __name__ == "__main__":
    m = ParameterMap()
    for line in sys.stdin:
        m.add_request(Request(line))

    if verbose:
        print "Requests:",m.request_count()

        for p in m.paths:
            print "Dumping:",p
            pprint.pprint(m.param_map(p))

    m.to_dot()
